#ifndef __Matrix4_hpp__
#define __Matrix4_hpp__

#include "Matrix3.hpp"
#include "Quaternion.hpp"

namespace ge {

//----------------------------------------------------------------------------//
// Matrix4
//----------------------------------------------------------------------------//

class Matrix4
{
 public:

     ///@todo add Matrix4& operator *= ( const Matrix4& _m )
     ///@todo add Matrix4& operator *= ( const Matrix3& _m )
     ///@todo add Matrix4 operator * ( const Quaternion& _q )
     ///@todo add friend Matrix4 operator * ( const Quaternion& _q, const Matrix4& _m )
     ///@todo add Matrix4& operator *= ( const Quaternion& _q )

     Matrix4( void );

     Matrix4( const Matrix4& _m );

     Matrix4( const Matrix3& _m );

     Matrix4( float _m00, float _m01, float _m02, float _m03, 
              float _m10, float _m11, float _m12, float _m13, 
              float _m20, float _m21, float _m22, float _m23, 
              float _m30, float _m31, float _m32, float _m33 );

     Matrix4& operator = ( const Matrix4& _m );

     Matrix4& operator = ( const Matrix3& _m );

     Matrix4& Set( float _m00, float _m01, float _m02, float _m03, 
                   float _m10, float _m11, float _m12, float _m13, 
                   float _m20, float _m21, float _m22, float _m23, 
                   float _m30, float _m31, float _m32, float _m33 );

     Matrix4& Set( const Matrix4& _m );

     Matrix4& Set( const Matrix3& _m );

     Matrix4& FromPtr( const float _v[16] );

     Matrix4& SetZero( void );

     Matrix4& SetIdentity( void );

     float* operator [] ( uint _row );

     const float* operator [] ( uint _row ) const;

     float& operator () ( uint _row, uint _col );

     const float& operator () ( uint _row, uint _col ) const;

     float* operator * ( void );

     const float* operator * ( void ) const;

     Vector3 operator * ( const Vector3& _v ) const;

     friend Vector3 operator * ( const Vector3& _v, const Matrix4& _m );

     Matrix4( const Quaternion& _q );

     operator Quaternion ( void ) const;

     Matrix4 Concatenate( const Matrix4& _m ) const;

     Matrix4 operator * ( const Matrix4& _m ) const;

     Matrix4 operator + ( const Matrix4& _m ) const;

     Matrix4 operator - ( const Matrix4& _m ) const;

     Matrix4& Transpose( void );

     Matrix4 TransposedCopy( void ) const;

     Matrix4& InsertMatrix3( const Matrix3& _m );

     Matrix4& FromMatrix3( const Matrix3& _m );

     Matrix3& ToMatrix3( Matrix3& _m ) const;

     Matrix3 ToMatrix3( void ) const;

     Matrix4& SetTrans( const Vector3& _v );

     Vector3 Trans( void ) const;

     Matrix4& MakeTrans( const Vector3& _v );

     Matrix4& SetScale( const Vector3& _v );

     Vector3 Scale( void ) const;

     Matrix4& MakeScale( const Vector3& _v );

     Matrix4 operator * ( float _s ) const;

     friend Matrix4 operator * ( float _s, const Matrix4& _m );

     Matrix4& SetOrientation( const Matrix3& _m );

     Matrix4& MakeRotation( const Matrix3& _m );

     Quaternion Orientation( void ) const;

     bool IsAffine( void ) const;

     Matrix4 ConcatenateAffine( const Matrix4& _m ) const;

     Vector3 TransformAffine( const Vector3& _v ) const;

     bool operator == ( const Matrix4& _m ) const;

     bool operator != ( const Matrix4& _m ) const;

     bool HasScale( void ) const;

     Matrix4& Inverse( void );

     Matrix4 InverseCopy( void ) const;

     static float _Minor( const Matrix4& _m, uint _r0, uint _r1, uint _r2, uint _c0, uint _c1, uint _c2 );

     Matrix4 Adjoint( void ) const;

     float Determinant( void ) const;

     bool HasNegativeScale( void ) const;

     Matrix4& InverseAffine( void );

     Matrix4 InverseAffineCopy( void ) const;

     Matrix4& MakeTransform( const Vector3& _position, const Vector3& _scale, const Quaternion& _orientation );

     Matrix4& MakeInverseTransform( const Vector3& _position, const Vector3& _scale, const Quaternion& _orientation );
      
     void Decomposition( Vector3& _position, Vector3& _scale, Quaternion& _orientation ) const;

     Matrix4& MakeOrtho2D( float _w, float _h );

     Matrix4& MakePerspective( float _fov, float _aspect, float _near, float _far );

     ///@todo add Vector3 Up()
     ///@todo add Vector3 Right()
     ///@todo add Vector3 Direction()
     ///@todo add LookAt()


 public:

     static const Matrix4 Zero;
     static const Matrix4 Identity;

 public:

     union
     {
          struct 
          { 
               float m00, m01, m02, m03; 
               float m10, m11, m12, m13; 
               float m20, m21, m22, m23; 
               float m30, m31, m32, m33; 
          };
          float m[4][4];
          float v[16];
     };
};

//----------------------------------------------------------------------------//
// Matrix4
//----------------------------------------------------------------------------//

//----------------------------------------------------------------------------//
inline Matrix4::Matrix4( void ) 
{
}
//----------------------------------------------------------------------------//
inline Matrix4::Matrix4( const Matrix4& _m )
{
     memcpy( v, _m.v, sizeof( float ) * 16 );
}
//----------------------------------------------------------------------------//
inline Matrix4::Matrix4( const Matrix3& _m )
{
     FromMatrix3( _m );    
}
//----------------------------------------------------------------------------//
inline Matrix4::Matrix4( float _m00, float _m01, float _m02, float _m03, 
                         float _m10, float _m11, float _m12, float _m13, 
                         float _m20, float _m21, float _m22, float _m23, 
                         float _m30, float _m31, float _m32, float _m33 ) : m00( _m00 ), m01( _m01 ), m02( _m02 ), m03( _m03 ),
                                                                            m10( _m10 ), m11( _m11 ), m12( _m12 ), m13( _m13 ),
                                                                            m20( _m20 ), m21( _m21 ), m22( _m22 ), m23( _m23 ),
                                                                            m30( _m30 ), m31( _m31 ), m32( _m32 ), m33( _m33 )
{
}
//----------------------------------------------------------------------------//
inline Matrix4& Matrix4::operator = ( const Matrix4& _m )
{
     memcpy( v, _m.v, sizeof( float ) * 16 );
     return *this;
}
//----------------------------------------------------------------------------//
inline Matrix4& Matrix4::operator = ( const Matrix3& _m )
{
     return FromMatrix3( _m );
}
//----------------------------------------------------------------------------//
inline Matrix4& Matrix4::Set( float _m00, float _m01, float _m02, float _m03, 
              float _m10, float _m11, float _m12, float _m13, 
              float _m20, float _m21, float _m22, float _m23, 
              float _m30, float _m31, float _m32, float _m33 )
              
{
     m00 = _m00; m01 = _m01; m02 = _m02; m03 = _m03;
     m10 = _m10; m11 = _m11; m12 = _m12; m13 = _m13;
     m20 = _m20; m21 = _m21; m22 = _m22; m23 = _m23;
     m30 = _m30; m31 = _m31; m32 = _m32; m33 = _m33;
     return *this;
}
//----------------------------------------------------------------------------//
inline Matrix4& Matrix4::Set( const Matrix4& _m )
{
     memcpy( v, _m.v, sizeof( float ) * 16 );
     return *this;
}
//----------------------------------------------------------------------------//
inline Matrix4& Matrix4::Set( const Matrix3& _m )
{
     return FromMatrix3( _m );
}
//----------------------------------------------------------------------------//
inline Matrix4& Matrix4::FromPtr( const float _v[16] )
{
     memcpy( v, _v, sizeof( float ) * 16 );
     return *this;
}
//----------------------------------------------------------------------------//
inline Matrix4& Matrix4::SetZero( void )
{
     return Set( 0.0f, 0.0f, 0.0f, 0.0f,
                 0.0f, 0.0f, 0.0f, 0.0f,
                 0.0f, 0.0f, 0.0f, 0.0f,
                 0.0f, 0.0f, 0.0f, 0.0f );
}
//----------------------------------------------------------------------------//
inline Matrix4& Matrix4::SetIdentity( void )
{
     return Set( 1.0f, 0.0f, 0.0f, 0.0f,
                 0.0f, 1.0f, 0.0f, 0.0f,
                 0.0f, 0.0f, 1.0f, 0.0f,
                 0.0f, 0.0f, 0.0f, 1.0f );
}
//----------------------------------------------------------------------------//
inline float* Matrix4::operator [] ( uint _row )
{
     return (float*)(m + _row);
}
//----------------------------------------------------------------------------//
inline const float* Matrix4::operator [] ( uint _row ) const
{
     return (float*)(m + _row);
}
//----------------------------------------------------------------------------//
inline float& Matrix4::operator () ( uint _row, uint _col )
{
     return m[_row][_col];
}
//----------------------------------------------------------------------------//
inline const float& Matrix4::operator () ( uint _row, uint _col ) const
{
     return m[_row][_col];
}
//----------------------------------------------------------------------------//
inline float* Matrix4::operator * ( void )
{
     return v;
}
//----------------------------------------------------------------------------//
inline const float* Matrix4::operator * ( void ) const
{
     return v;
}
//----------------------------------------------------------------------------//
inline Vector3 Matrix4::operator * ( const Vector3& _v ) const
{
     float _iw( 1.0f / ( m30 * _v.x + m31 * _v.y + m32 * _v.z + m33 ) );

     return Vector3( ( m00 * _v.x + m01 * _v.y + m02 * _v.z + m03 ) * _iw,
                     ( m10 * _v.x + m11 * _v.y + m12 * _v.z + m13 ) * _iw,
                     ( m20 * _v.x + m21 * _v.y + m22 * _v.z + m23 ) * _iw );
}
//----------------------------------------------------------------------------//
inline Vector3 operator * ( const Vector3& _v, const Matrix4& _m )
{
     return _m * _v;
}
//----------------------------------------------------------------------------//
inline Matrix4::Matrix4( const Quaternion& _q )
{
     FromMatrix3( _q.ToRotationMatrix() );
}
//----------------------------------------------------------------------------//
inline Matrix4::operator Quaternion ( void ) const
{
     return Orientation();
}
//----------------------------------------------------------------------------//
inline Matrix4 Matrix4::Concatenate( const Matrix4& _m ) const
{
     return Matrix4( m00 * _m.m00 + m01 * _m.m10 + m02 * _m.m20 + m03 * _m.m30,
                     m00 * _m.m01 + m01 * _m.m11 + m02 * _m.m21 + m03 * _m.m31,
                     m00 * _m.m02 + m01 * _m.m12 + m02 * _m.m22 + m03 * _m.m32,
                     m00 * _m.m03 + m01 * _m.m13 + m02 * _m.m23 + m03 * _m.m33,
                     m10 * _m.m00 + m11 * _m.m10 + m12 * _m.m20 + m13 * _m.m30,
                     m10 * _m.m01 + m11 * _m.m11 + m12 * _m.m21 + m13 * _m.m31,
                     m10 * _m.m02 + m11 * _m.m12 + m12 * _m.m22 + m13 * _m.m32,
                     m10 * _m.m03 + m11 * _m.m13 + m12 * _m.m23 + m13 * _m.m33,
                     m20 * _m.m00 + m21 * _m.m10 + m22 * _m.m20 + m23 * _m.m30,
                     m20 * _m.m01 + m21 * _m.m11 + m22 * _m.m21 + m23 * _m.m31,
                     m20 * _m.m02 + m21 * _m.m12 + m22 * _m.m22 + m23 * _m.m32,
                     m20 * _m.m03 + m21 * _m.m13 + m22 * _m.m23 + m23 * _m.m33,
                     m30 * _m.m00 + m31 * _m.m10 + m32 * _m.m20 + m33 * _m.m30,
                     m30 * _m.m01 + m31 * _m.m11 + m32 * _m.m21 + m33 * _m.m31,
                     m30 * _m.m02 + m31 * _m.m12 + m32 * _m.m22 + m33 * _m.m32,
                     m30 * _m.m03 + m31 * _m.m13 + m32 * _m.m23 + m33 * _m.m33 );
}
//----------------------------------------------------------------------------//
inline Matrix4 Matrix4::operator * ( const Matrix4& _m ) const
{
     return Concatenate( _m );
}
//----------------------------------------------------------------------------//
inline Matrix4 Matrix4::operator + ( const Matrix4& _m ) const
{
     return Matrix4( m00 + _m.m00, m01 + _m.m01, m02 + _m.m02, m03 + _m.m03,
                     m10 + _m.m10, m11 + _m.m11, m12 + _m.m12, m13 + _m.m13,
                     m20 + _m.m20, m21 + _m.m21, m22 + _m.m22, m23 + _m.m23,
                     m30 + _m.m30, m31 + _m.m31, m32 + _m.m32, m33 + _m.m33 );
}
//----------------------------------------------------------------------------//
inline Matrix4 Matrix4::operator - ( const Matrix4& _m ) const
{
     return Matrix4( m00 - _m.m00, m01 - _m.m01, m02 - _m.m02, m03 - _m.m03,
                     m10 - _m.m10, m11 - _m.m11, m12 - _m.m12, m13 - _m.m13,
                     m20 - _m.m20, m21 - _m.m21, m22 - _m.m22, m23 - _m.m23,
                     m30 - _m.m30, m31 - _m.m31, m32 - _m.m32, m33 - _m.m33 );
}
//----------------------------------------------------------------------------//
inline Matrix4& Matrix4::Transpose( void )
{
     Swap( m01, m10 );
     Swap( m02, m20 );
     Swap( m03, m30 );
     Swap( m12, m21 );
     Swap( m13, m31 );
     Swap( m23, m32 );
     return *this;
}
//----------------------------------------------------------------------------//
inline Matrix4 Matrix4::TransposedCopy( void ) const
{
     return Matrix4( *this ).Transpose();
}
//----------------------------------------------------------------------------//
inline Matrix4& Matrix4::InsertMatrix3( const Matrix3& _m )
{
     m00 = _m.m00; m01 = _m.m01; m02 = _m.m02;     
     m10 = _m.m10; m01 = _m.m11; m12 = _m.m12;     
     m20 = _m.m20; m01 = _m.m21; m22 = _m.m22; 
     return *this;
}
//----------------------------------------------------------------------------//
inline Matrix4& Matrix4::FromMatrix3( const Matrix3& _m )
{
     m00 = _m.m00; m01 = _m.m01; m02 = _m.m02; m03 = 0.0f;     
     m10 = _m.m10; m11 = _m.m11; m12 = _m.m12; m13 = 0.0f;      
     m20 = _m.m20; m21 = _m.m21; m22 = _m.m22; m23 = 0.0f;  
     m30 = 0.0f;   m31 = 0.0f;   m32 = 0.0f;   m33 = 1.0f;  
     return *this;
}
//----------------------------------------------------------------------------//
inline Matrix3& Matrix4::ToMatrix3( Matrix3& _m ) const
{
     return _m.Set( m00, m01, m02, m10, m11, m12, m20, m21, m22 );
}
//----------------------------------------------------------------------------//
inline Matrix3 Matrix4::ToMatrix3( void ) const
{
     return Matrix3( m00, m01, m02, m10, m11, m12, m20, m21, m22 );
}
//----------------------------------------------------------------------------//
inline Matrix4& Matrix4::SetTrans( const Vector3& _v )
{
     m03 = _v.x;
     m13 = _v.y;
     m23 = _v.z;

     return *this;
}
//----------------------------------------------------------------------------//
inline Vector3 Matrix4::Trans( void ) const
{
     return Vector3( m03, m13, m23 );
}
//----------------------------------------------------------------------------//
inline Matrix4& Matrix4::MakeTrans( const Vector3& _v )
{
     return Set( 1.0f, 0.0f, 0.0f, _v.x,
                 0.0f, 1.0f, 0.0f, _v.y,
                 0.0f, 0.0f, 1.0f, _v.z,
                 0.0f, 0.0f, 0.0f, 1.0f );
}
//----------------------------------------------------------------------------//
inline Matrix4& Matrix4::SetScale( const Vector3& _v )
{
     m00 = _v.x;
     m11 = _v.y;
     m22 = _v.z;
     return *this;
}
//----------------------------------------------------------------------------//
inline Vector3 Matrix4::Scale( void ) const
{
     return Vector3( m00, m11, m22 );
}
//----------------------------------------------------------------------------//
inline Matrix4& Matrix4::MakeScale( const Vector3& _v )
{
     return Set( _v.x, 0.0f, 0.0f, 0.0f,
                 0.0f, _v.y, 0.0f, 0.0f,
                 0.0f, 0.0f, _v.z, 0.0f,
                 0.0f, 0.0f, 0.0f, 1.0f );
}
//----------------------------------------------------------------------------//
inline Matrix4 Matrix4::operator * ( float _s ) const
{
     return Matrix4( _s * m00, _s * m01, _s * m02, _s * m03,
                     _s * m10, _s * m11, _s * m12, _s * m13,
                     _s * m20, _s * m21, _s * m22, _s * m23,
                     _s * m30, _s * m31, _s * m32, _s * m33 );
}
//----------------------------------------------------------------------------//
inline Matrix4 operator * ( float _s, const Matrix4& _m )
{
     return _m * _s;
}
//----------------------------------------------------------------------------//
inline Matrix4& Matrix4::SetOrientation( const Matrix3& _m )
{
 // ?? *scale
     return InsertMatrix3( _m );
}
//----------------------------------------------------------------------------//
inline Matrix4& Matrix4::MakeRotation( const Matrix3& _m )
{
     return Set( m00,  m01,  m02,  0.0f,
                 m10,  m11,  m12,  0.0f,
                 m20,  m21,  m22,  0.0f,
                 0.0f, 0.0f, 0.0f, 1.0f );
}
//----------------------------------------------------------------------------//
inline Quaternion Matrix4::Orientation( void ) const
{
     return ToMatrix3();
}
//----------------------------------------------------------------------------//
inline bool Matrix4::IsAffine( void ) const
{
     return ( m30 == 0.0f && m31 == 0.0f && m32 == 0.0f && m33 == 1.0f );
}
//----------------------------------------------------------------------------//
inline Matrix4 Matrix4::ConcatenateAffine( const Matrix4& _m ) const
{
     assert( IsAffine() && _m.IsAffine() );
     return Matrix4(  m00 * _m.m00 + m01 * _m.m10 + m02 * _m.m20,
                      m00 * _m.m01 + m01 * _m.m11 + m02 * _m.m21,
                      m00 * _m.m02 + m01 * _m.m12 + m02 * _m.m22,
                      m00 * _m.m03 + m01 * _m.m13 + m02 * _m.m23 + m03,
                      m10 * _m.m00 + m11 * _m.m10 + m12 * _m.m20,
                      m10 * _m.m01 + m11 * _m.m11 + m12 * _m.m21,
                      m10 * _m.m02 + m11 * _m.m12 + m12 * _m.m22,
                      m10 * _m.m03 + m11 * _m.m13 + m12 * _m.m23 + m13,
                      m20 * _m.m00 + m21 * _m.m10 + m22 * _m.m20,
                      m20 * _m.m01 + m21 * _m.m11 + m22 * _m.m21,
                      m20 * _m.m02 + m21 * _m.m12 + m22 * _m.m22,
                      m20 * _m.m03 + m21 * _m.m13 + m22 * _m.m23 + m23,
                      0.0f, 0.0f, 0.0f, 1.0f );
}
//----------------------------------------------------------------------------//
inline Vector3 Matrix4::TransformAffine( const Vector3& _v ) const
{
     assert( IsAffine() );

     return Vector3( m00 * _v.x + m01 * _v.y + m02 * _v.z + m03, 
                     m10 * _v.x + m11 * _v.y + m12 * _v.z + m13,
                     m20 * _v.x + m21 * _v.y + m22 * _v.z + m23 );
}
//----------------------------------------------------------------------------//
inline bool Matrix4::operator == ( const Matrix4& _m ) const
{
     return ( m00 == _m.m00 && m01 == _m.m01 && m02 == _m.m02 && m03 == _m.m03 &&
              m10 == _m.m10 && m11 == _m.m11 && m12 == _m.m12 && m13 == _m.m13 &&
              m20 == _m.m20 && m21 == _m.m21 && m22 == _m.m22 && m23 == _m.m23 &&
              m30 == _m.m30 && m31 == _m.m31 && m32 == _m.m32 && m33 == _m.m33 );
}
//----------------------------------------------------------------------------//
inline bool Matrix4::operator != ( const Matrix4& _m ) const
{
     return !(*this == _m);
}
//----------------------------------------------------------------------------//
inline bool Matrix4::HasScale( void ) const
{
     if( !Math::Equals( m00 * m00 + m10 * m10 + m20 * m20, 1.0f, Math::Epsilon ) ) return true;
     if( !Math::Equals( m01 * m01 + m11 * m11 + m21 * m21, 1.0f, Math::Epsilon ) ) return true;
     if( !Math::Equals( m02 * m02 + m12 * m12 + m22 * m22, 1.0f, Math::Epsilon ) ) return true;

     return false;
}
//----------------------------------------------------------------------------//
inline Matrix4& Matrix4::Inverse( void )
{
     float _m00 = m00, _m01 = m01, _m02 = m02, _m03 = m03;
     float _m10 = m10, _m11 = m11, _m12 = m12, _m13 = m13;
     float _m20 = m20, _m21 = m21, _m22 = m22, _m23 = m23;
     float _m30 = m30, _m31 = m31, _m32 = m32, _m33 = m33;

     float _v0 = ( _m20 * _m31 - _m21 * _m30 );
     float _v1 = ( _m20 * _m32 - _m22 * _m30 );
     float _v2 = ( _m20 * _m33 - _m23 * _m30 );
     float _v3 = ( _m21 * _m32 - _m22 * _m31 );
     float _v4 = ( _m21 * _m33 - _m23 * _m31 );
     float _v5 = ( _m22 * _m33 - _m23 * _m32 );

     float _t00 = + ( _v5 * _m11 - _v4 * _m12 + _v3 * _m13 );
     float _t10 = - ( _v5 * _m10 - _v2 * _m12 + _v1 * _m13 );
     float _t20 = + ( _v4 * _m10 - _v2 * _m11 + _v0 * _m13 );
     float _t30 = - ( _v3 * _m10 - _v1 * _m11 + _v0 * _m12 );

     float _invdet = 1.0f / ( _t00 * _m00 + _t10 * _m01 + _t20 * _m02 + _t30 * _m03 );

     m00 = _t00 * _invdet;
     m10 = _t10 * _invdet;
     m20 = _t20 * _invdet;
     m30 = _t30 * _invdet;

     m01 = - ( _v5 * _m01 - _v4 * _m02 + _v3 * _m03 ) * _invdet;
     m11 = + ( _v5 * _m00 - _v2 * _m02 + _v1 * _m03 ) * _invdet;
     m21 = - ( _v4 * _m00 - _v2 * _m01 + _v0 * _m03 ) * _invdet;
     m31 = + ( _v3 * _m00 - _v1 * _m01 + _v0 * _m02 ) * _invdet;

     _v0 = ( _m10 * _m31 - _m11 * _m30 );
     _v1 = ( _m10 * _m32 - _m12 * _m30 );
     _v2 = ( _m10 * _m33 - _m13 * _m30 );
     _v3 = ( _m11 * _m32 - _m12 * _m31 );
     _v4 = ( _m11 * _m33 - _m13 * _m31 );
     _v5 = ( _m12 * _m33 - _m13 * _m32 );

     m02 = + ( _v5 * _m01 - _v4 * _m02 + _v3 * _m03 ) * _invdet;
     m12 = - ( _v5 * _m00 - _v2 * _m02 + _v1 * _m03 ) * _invdet;
     m22 = + ( _v4 * _m00 - _v2 * _m01 + _v0 * _m03 ) * _invdet;
     m32 = - ( _v3 * _m00 - _v1 * _m01 + _v0 * _m02 ) * _invdet;

     _v0 = ( _m21 * _m10 - _m20 * _m11 );
     _v1 = ( _m22 * _m10 - _m20 * _m12 );
     _v2 = ( _m23 * _m10 - _m20 * _m13 );
     _v3 = ( _m22 * _m11 - _m21 * _m12 );
     _v4 = ( _m23 * _m11 - _m21 * _m13 );
     _v5 = ( _m23 * _m12 - _m22 * _m13 );

     m03 = - ( _v5 * _m01 - _v4 * _m02 + _v3 * _m03 ) * _invdet;
     m13 = + ( _v5 * _m00 - _v2 * _m02 + _v1 * _m03 ) * _invdet;
     m23 = - ( _v4 * _m00 - _v2 * _m01 + _v0 * _m03 ) * _invdet;
     m33 = + ( _v3 * _m00 - _v1 * _m01 + _v0 * _m02 ) * _invdet;

     return *this;
}
//----------------------------------------------------------------------------//
inline Matrix4 Matrix4::InverseCopy( void ) const
{
     return Matrix4( *this ).Inverse();
}
//----------------------------------------------------------------------------//
inline float Matrix4::_Minor( const Matrix4& _m, uint _r0, uint _r1, uint _r2, uint _c0, uint _c1, uint _c2 )
{
     return ( _m( _r0, _c0 ) * ( _m( _r1, _c1 ) * _m( _r2, _c2 ) - _m( _r2, _c1 ) * _m( _r1, _c2 ) ) -
              _m( _r0, _c1 ) * ( _m( _r1, _c0 ) * _m( _r2, _c2 ) - _m( _r2, _c0 ) * _m( _r1, _c2 ) ) +
              _m( _r0, _c2 ) * ( _m( _r1, _c0 ) * _m( _r2, _c1 ) - _m( _r2, _c0 ) * _m( _r1, _c1 ) ) );
}
//----------------------------------------------------------------------------//
inline Matrix4 Matrix4::Adjoint( void ) const
{
     Matrix4( _Minor( *this, 1, 2, 3, 1, 2, 3 ),
             -_Minor( *this, 0, 2, 3, 1, 2, 3 ),
              _Minor( *this, 0, 1, 3, 1, 2, 3 ),
             -_Minor( *this, 0, 1, 2, 1, 2, 3 ),
             -_Minor( *this, 1, 2, 3, 0, 2, 3 ),
              _Minor( *this, 0, 2, 3, 0, 2, 3 ),
             -_Minor( *this, 0, 1, 3, 0, 2, 3 ),
              _Minor( *this, 0, 1, 2, 0, 2, 3 ),
              _Minor( *this, 1, 2, 3, 0, 1, 3 ),
             -_Minor( *this, 0, 2, 3, 0, 1, 3 ),
              _Minor( *this, 0, 1, 3, 0, 1, 3 ),
             -_Minor( *this, 0, 1, 2, 0, 1, 3 ),
             -_Minor( *this, 1, 2, 3, 0, 1, 2 ),
              _Minor( *this, 0, 2, 3, 0, 1, 2 ),
             -_Minor( *this, 0, 1, 3, 0, 1, 2 ),
              _Minor( *this, 0, 1, 2, 0, 1, 2 ) );
}
//----------------------------------------------------------------------------//
inline float Matrix4::Determinant( void ) const
{
     return ( m00 * _Minor( *this, 1, 2, 3, 1, 2, 3 ) -
              m01 * _Minor( *this, 1, 2, 3, 0, 2, 3 ) +
              m02 * _Minor( *this, 1, 2, 3, 0, 1, 3 ) -
              m03 * _Minor( *this, 1, 2, 3, 0, 1, 2 ) );
}
//----------------------------------------------------------------------------//
inline bool Matrix4::HasNegativeScale( void ) const
{
     return Determinant() < 0.0f;
}
//----------------------------------------------------------------------------//
inline Matrix4& Matrix4::InverseAffine( void )
{
     assert( IsAffine() );

     float _m00 = m00, _m01 = m01, _m02 = m02;
     float _m10 = m10, _m11 = m11, _m12 = m12;
     float _m20 = m20, _m21 = m21, _m22 = m22;
     float _m03 = m03, _m13 = m13, _m23 = m23;

     float _t00 = ( _m22 * _m11 - _m21 * _m12 );
     float _t10 = ( _m20 * _m12 - _m22 * _m10 );
     float _t20 = ( _m21 * _m10 - _m20 * _m11 );

     float _invdet = 1.0f / ( _m00 * _t00 + _m01 * _t10 + _m02 * _t20 );

     _t00 *= _invdet; _t10 *= _invdet; _t20 *= _invdet;

     _m00 *= _invdet; _m01 *= _invdet; _m02 *= _invdet;

     m00 = _t00;
     m01 = ( _m02 * _m21 ) - ( _m01 * _m22 );
     m02 = ( _m01 * _m12 ) - ( _m02 * _m11 );

     m10 = _t10;
     m11 = ( _m00 * _m22 ) - ( _m02 * _m20 );
     m12 = ( _m02 * _m10 ) - ( _m00 * _m12 );

     m20 = _t20;
     m21 = ( _m01 * _m20 ) - ( _m00 * _m21 );
     m22 = ( _m00 * _m11 ) - ( _m01 * _m10 );

     m03 = - ( m00 * _m03 + m01 * _m13 + m02 * _m23 );
     m13 = - ( m10 * _m03 + m11 * _m13 + m12 * _m23 );
     m23 = - ( m20 * _m03 + m21 * _m13 + m22 * _m23 );

     m30 = 0.0f;
     m31 = 0.0f;
     m32 = 0.0f;
     m33 = 1.0f;

     return *this;
}
//----------------------------------------------------------------------------//
inline Matrix4 Matrix4::InverseAffineCopy( void ) const
{
     return Matrix4( *this ).InverseAffine();
}
//----------------------------------------------------------------------------//
inline Matrix4& Matrix4::MakeTransform( const Vector3& _position, const Vector3& _scale, const Quaternion& _orientation )
{
     // Ordering:
     //    1. Scale
     //    2. Rotate
     //    3. Translate

     Matrix3 _r( _orientation );

     return Set( _scale.x * _r.m00, _scale.y * _r.m01, _scale.z * _r.m02, _position.x,
                 _scale.x * _r.m10, _scale.y * _r.m11, _scale.z * _r.m12, _position.y,
                 _scale.x * _r.m20, _scale.y * _r.m21, _scale.z * _r.m22, _position.z,
                 0.0f,              0.0f,              0.0f,              1.0f );
}
//----------------------------------------------------------------------------//
inline Matrix4& Matrix4::MakeInverseTransform( const Vector3& _position, const Vector3& _scale, const Quaternion& _orientation )
{
     Vector3 _inv_scale( 1.0f / _scale );
     Quaternion _inv_orientation = _orientation.InverseCopy();
     Vector3 _inv_position( ( _inv_orientation * ( -_position ) ) * _inv_scale );

     return MakeTransform( _inv_position, _inv_scale, _inv_orientation );
}
//----------------------------------------------------------------------------//
inline void Matrix4::Decomposition( Vector3& _position, Vector3& _scale, Quaternion& _orientation ) const
{
     assert( IsAffine() );

     Vector3 _u;
     Matrix3 _q;
     ToMatrix3().Decomposition( _q, _scale, _u ); 
     _orientation = _q;
     _position = Vector3( m03, m13, m23 );
}
//----------------------------------------------------------------------------//
inline Matrix4& Matrix4::MakeOrtho2D( float _w, float _h )
{
     if( _w == 0.0f ) { _w = 800.0f; }
     if( _h == 0.0f ) { _w = 600.0f; }

     m00 = 2.0f / _w; m01 = 0.0f;       m02 = 0.0f;  m03 = -1.0f;
     //_10 = 0.0f;      _11 = 2.0f / _h; _12 = 0.0f;  _13 = -1.0f; // origin = left,bottom
     m10 = 0.0f;      m11 = -2.0f / _h; m12 = 0.0f;  m13 = 1.0f; // origin = left,top
     m20 = 0.0f;      m21 = 0.0f;       m22 = -1.0f; m23 = 0.0f;
     m30 = 0.0f;      m31 = 0.0f;       m32 = 0.0f;  m33 = 1.0f;

     return *this;
}
//----------------------------------------------------------------------------//
inline Matrix4& Matrix4::MakePerspective( float _fov, float _aspect, float _near, float _far )
{           
     float _h = 1.0f / Math::Tan( _fov * 0.5f );
     float _w = _h / _aspect;
     float _q = -( _far + _near ) / ( _far - _near );
     float _qn = -2.0f * ( _far * _near ) / ( _far - _near );

     m00 = _w;   m01 = 0.0f; m02 = 0.0f;  m03 = 0.0f;
     m10 = 0.0f; m11 = _h;   m12 = 0.0f;  m13 = 0.0f;
     m20 = 0.0f; m21 = 0.0f; m22 = _q;    m23 = _qn;
     m30 = 0.0f; m31 = 0.0f; m32 = -1.0f; m33 = 0.0f;

     return *this;
}
//----------------------------------------------------------------------------//

} // namespace ge

#endif//__Matrix4_hpp__
